package Logica;

import General.Constantes;
import Logica.Celdas.Celda;
import Logica.Enemigos.Enemigo;
import Logica.Enemigos.Monkey;
import Logica.PowerUps.PowerUp;

/**
 * Esta clase es la encargada de realizar los movimientos de los objetos del juego. Utiliza a un
 * ControlColisiones para poder realizar los moviemientos y verificar que no hallan colisiones.
 * 
 * @author Mariano Rodecker
 * @author Matias Varela
 * 
 */
public class Inteligencia {

	/* ################## ATRIBUTOS DE INSTANCIA ######################## */
	protected ControlColisiones cCol;
	protected Puntuacion puntuacion;
	
	/* ################## CONSTRUCTORES ######################## */
	/**
	 * Constructor para una inteligencia.
	 * 
	 * @param cCol
	 *            - Es el control de colisiones que necesita la inteligencia para funcionar.
	 */
	public Inteligencia() {

	}

	/* ################## CONSULTAS ######################## */
	
	public ControlColisiones getControlColisiones() {
		return cCol;
	}

	public Puntuacion getPuntuacion() {
		return puntuacion;
	}

	/**
	 * Retorna si un objeto obj puede moverse en su direccion actual.
	 * 
	 * @param obj
	 *            - Objeto a verificar si puede moverse.
	 * @return true si el objeto puede moverse; false en otro caso.
	 */

	public boolean puedeMover(Objeto obj) {
		Celda c = cCol.getColisionCelda(obj, obj.getDirActual());
		if (c != null)
			return false;
		else
			return true;
	}

	/**
	 * Retorna si un objeto obj puede moverse en la direccion pasada por parametro
	 * 
	 * @param obj
	 *            - Objeto a verificar si puede moverse.
	 * @param dir
	 *            - direccion a la cual intenta mover el objeto.
	 * @return true si el objeto puede moverse; false en otro caso.
	 */
	public boolean puedeMover(Objeto obj, int dir) {
		Celda c = cCol.getColisionCelda(obj, dir);
		if (c != null)
			return false;
		else
			return true;
	}

	/**
	 * Retorna si un personaje p puede moverse en la direccion pasada por parametro
	 * 
	 * @param p
	 *            - Personaje a verificar si puede moverse.
	 * @param dir
	 *            - direccion a la cual intenta mover el personaje.
	 * @return true si el personaje puede moverse; false en otro caso.
	 */
	public boolean puedeMover(Personaje p, int dir) {
		Celda c = cCol.getColisionCelda(p, dir);
		if (c != null) {
			c.modificar(p);
			return false;
		} else
			return true;
	}

	/* ################## COMANDOS ######################## */
	
	public void insertarPowerUpEnMapaVisible(PowerUp e) {
		cCol.getMapaVisible().agregarPowerUp(e);
	}

	public void setPuntuacion(Puntuacion puntuacion) {
		this.puntuacion = puntuacion;
	}

	public void setControlColisiones(ControlColisiones cCol) {
		this.cCol = cCol;
	}

	/**
	 * Esto permite mover al Mario, en alguna de las cuatro direcciones.
	 * 
	 * @param m
	 *            - Mario a mover.
	 * @return true si pudo mover a Mario; false en otro caso.
	 */
	public boolean mover(Mario m) {
		Celda c;
		/*
		 * Si Mario no esta saltando y ademas puede moverse hacia abajo entonces significa que Mario
		 * cayendo.
		 */
		if (!m.isSaltando()) {
			c = cCol.getColisionCelda(m, Constantes.dir_bot);
			if (c == null) { // esto significa que no se produjo una colision
				m.caer();
			}
			/*
			 * si se produce alguna colision con alguna celda, entonces la celda debe modificar al
			 * mario
			 */
			else {
				if (m.getDirActual() == Constantes.dir_top)
					m.setDirActual(Constantes.dir_right);
				
				c.modificar(m);
			}

			c = null;
		}
		/*
		 * Verifico si Mario puede moverse en su direccion actual. En caso que halla una colision
		 * entonces la resuelvo.
		 */
		boolean puedeMoverse = true;
		c = cCol.getColisionCelda(m, m.getDirActual());
		if (c != null) { // esto significa que hay una colision, con la celda c
			/*
			 * Si Mario colisiona con una celda que es un ladrillo y ademas su direccion es hacia
			 * arriba entonces significa que Mario golpeo al ladrillo en la parte inferior, por lo
			 * tanto el ladrillo es golpeado.
			 */
			if (m.getDirActual() == Constantes.dir_top && !m.isCayendo()) {
				m.getCaracteristica().golpear(c);			
			}

			c.modificar(m);
			/*
			 * Como hay una colision entonces Mario no puede moverse en su direccion actual
			 */
			puedeMoverse = false;
		}

		/*
		 * Si puede moverse Mario en su direccion actual, entonces realizo el movimiento
		 * correspondiente. Retornando si se puede mover o no.
		 */
		if (puedeMoverse) {
			switch (m.getDirActual()) {
				case Constantes.dir_right:
					m.mover(1, 0);
					return true;
				case Constantes.dir_bot:
					m.mover(0, 1);
					return true;
				case Constantes.dir_left:
					/*
					 * antes de mover hacia la izquierda verifico que Mario no se pase del
					 * MapaVisible
					 */
					if (m.getX() > cCol.getMapaVisible().getInicio()) {
						m.mover(-1, 0);
						return true;
					} else
						/*
						 * No puede moverse ya que Mario no tiene permitido moverse hacia la
						 * izquierda del MapaVisible
						 */
						return false;
				case Constantes.dir_top:
					m.mover(0, -1);
					return true;
			}
		}
		return false;
	}

	public void mover(Enemigo enemigo) {
		// Siempre contemplamos que se pueda mover para abajo.
		// Esto hace que el enemigo siempre este apoyado sobre una superficie.
		if (puedeMover(enemigo, Constantes.dir_bot))
			enemigo.mover(0, 1);

		if (puedeMover(enemigo)) {
			switch (enemigo.getDirActual()) {
				case Constantes.dir_right:
					enemigo.mover(1, 0);
					break;
				case Constantes.dir_bot:
					enemigo.mover(0, 1);
					break;
				case Constantes.dir_left:
					enemigo.mover(-1, 0);
					break;
				case Constantes.dir_top:
					enemigo.mover(0, -1);
					break;
			}
		} else {
			int actual = enemigo.getDirActual();
			if (actual == Constantes.dir_left || actual == Constantes.dir_right) {
				int dOpuesta = this.getDirOpuesta(enemigo.getDirActual());
				if (puedeMover(enemigo, dOpuesta)) {
					enemigo.setDirActual(dOpuesta);
					switch (enemigo.getDirActual()) {
						case Constantes.dir_right:
							enemigo.mover(1, 0);
							break;
						case Constantes.dir_bot:
							enemigo.mover(0, 1);
							break;
						case Constantes.dir_left:
							enemigo.mover(-1, 0);
							break;
						case Constantes.dir_top:
							enemigo.mover(0, -1);
							break;
					}
				}
			}
		}
		if (cCol.getColisionMario(enemigo, enemigo.getDirActual())) {
			cCol.getMapaVisible().getMario().getCaracteristica().colisionarEnemigo(enemigo);
		}

	}

	public void mover(PowerUp pw) {
		if (puedeMover(pw, Constantes.dir_bot))
			pw.mover(0, 1);

		if (puedeMover(pw)) {
			switch (pw.getDirActual()) {
				case Constantes.dir_right:
					pw.mover(1, 0);
					break;
				case Constantes.dir_bot:
					pw.mover(0, 1);
					break;
				case Constantes.dir_left:
					pw.mover(-1, 0);
					break;
				case Constantes.dir_top:
					pw.mover(0, -1);
					break;
			}
		} else {
			int actual = pw.getDirActual();
			if (actual == Constantes.dir_left || actual == Constantes.dir_right) {
				int dOpuesta = this.getDirOpuesta(pw.getDirActual());
				if (puedeMover(pw, dOpuesta)) {
					pw.setDirActual(dOpuesta);
					switch (pw.getDirActual()) {
						case Constantes.dir_right:
							pw.mover(1, 0);
							break;
						case Constantes.dir_bot:
							pw.mover(0, 1);
							break;
						case Constantes.dir_left:
							pw.mover(-1, 0);
							break;
						case Constantes.dir_top:
							pw.mover(0, -1);
							break;
					}
				}
			}
		}
		if (cCol.getColisionMario(pw, pw.getDirActual())) {
			cCol.getMapaVisible().getMario().aplicarCaracteristica(pw);

			cCol.getMapaVisible().quitarPowerUp(pw);
		}
	}

	public void mover(Proyectil p) {
		/*
		 * if colisiona con enemigo; matarlo y desaparcer; sino ;if colisiona con celda; desaparecer
		 * sino moverlo en una direccion fin si
		 */
		Enemigo e = cCol.getColisionEnemigo(p, p.dirActual);
		if (e != null) {
			this.cCol.getMapaVisible().getMario().matarEnemigo(e);
			p.Quitar();
		} else {
			if (!puedeMover(p)) {
				p.Quitar();
			} else {
				switch (p.getDirActual()) {
					case Constantes.dir_right:
						p.mover(1, 0);
						break;
					case Constantes.dir_bot:
						p.mover(0, 1);
						break;
					case Constantes.dir_left:
						p.mover(-1, 0);
						break;
					case Constantes.dir_top:
						p.mover(0, -1);
						break;
				}
			}

		}

	}
	
	/*
	 * ############## METODOS AUXILIARES DE LA CLASE ##########
	 */

	/* Dada una direccion pasada por parametro retorna su direccion opuesta */
	private int getDirOpuesta(int dir) {

		switch (dir) {
			case Constantes.dir_right:
				return Constantes.dir_left;
			case Constantes.dir_left:
				return Constantes.dir_right;
			case Constantes.dir_bot:
				return Constantes.dir_top;
			case Constantes.dir_top:
				return Constantes.dir_bot;

		}
		return dir;
	}

}
